package ReefStats::Controller::Graph;

use strict;
use warnings;
use base 'Catalyst::Controller';
use GD::Graph::bars;
use GD::Graph::pie;
use DateTime;
use List::MoreUtils qw(mesh);

sub reasons : Local {
    my ( $self, $c ) = @_;

    my $cluster    = $c->req->param('cluster');
    my $domain     = $c->req->param('domain');
    my $user       = $c->req->param('user');
    my $start_date = $c->req->param('start_date');
    my $end_date   = $c->req->param('end_date');

    my ( $upper_date, $lower_date );

    my @x = qw(month day year);
    if ($end_date) {
        my @y = split /\D/, $end_date;
        $upper_date = DateTime->new( mesh @x, @y );
    }
    else {
        $upper_date = DateTime->now()->subtract( days => 1 );
    }

    if ($start_date) {
        my @y = split /\D/, $start_date;
        $lower_date = DateTime->new( mesh @x, @y );
    }
    else {
        $lower_date = $upper_date->clone->subtract( days => 7 );
    }

    $upper_date->set_hour(23)->set_minute(59)->set_second(59)
      ->set_nanosecond(999999999);
    $lower_date->set_hour(0)->set_minute(0)->set_second(0)->set_nanosecond(0);

    my $search_criteria =
      { day => { -between => [ $lower_date, $upper_date ] }, };

    my @metrics = (
        [ blacklist        => Blacklist          => q~'Blacklist'~ ],
        [ spam_fingerprint => 'SPAM Fingerprint' => q~'SPAM Fingerprint'~ ],
        [
            invalid_recipient => 'Invalid Recipient' =>
              q~'Recipient Address Rejected','Invalid Recipient'~
        ],
        [
            intention_analysis => 'Intention Analysis' =>
              q~'Intention Analysis'~
        ],
        [
            header_filter_match => 'Header Filter Match' =>
              q~'Header Filter Match'~
        ],
        [
            barracuda_blocklist => 'Barracuda Blocklist' =>
              q~'Barracuda Blocklist'~
        ],
        [ timeout_exceeded => 'Timeout Exceeded' => q~'Timeout Exceeded'~ ],
        [ allowed          => Allowed            => q~'(Allowed)'~ ],
        [
            barracuda_whitelist => 'Barracuda Whitelist' =>
              q~'Barracuda Whitelist'~
        ],
        [
            recipient_whitelist => 'Recipient Whitelist' =>
q~'Sender Block/Accept', 'Sender Address Rejected') and action in ('Whitelist'~
        ],
        [
            other => 'Other' =>
q~'Domain Not Found', 'Mail Protocol Error', 'Sender Address Rejected', 'Sender Block/Accept', 'Subject Filter Match', 'Virus', 'Client IP', 'Banned Attachment', 'No Such Domain', ''~
        ],
        [ score => 'Score' => q~'Score'~ ],
        [
            recipient_blocked => 'Recipient Blocked' =>
              q~'Sender Block/Accept') and action in ('Blocked'~
        ],
    );

    $search_criteria->{cluster} = $cluster if $cluster;
    $search_criteria->{domain} = $domain
      if $domain and not $user;
    $search_criteria->{user} = $user if $user;

    my $table =
        ($user)   ? 'ActionsReasonsUser'
      : ($domain) ? 'ActionsReasonsDomain'
      :             'ActionsReasonsAll';

    $c->log->info("table [$table]");

    my $maillog = $c->model($table);

    my $rs = $maillog->search(
        {%$search_criteria},
        {
            select => [
                'day',
                map {
                    {       sum => 'case when reason in ('
                          . $_->[2]
                          . ') then number else 0 end' }
                  } @metrics
            ],
            as       => [ 'day', map { $_->[0] } @metrics ],
            group_by => [qw( day )],
            order_by => [qw( day )],
        }
    );

    my @results = ( [] );
    push @results, [] for @metrics;
    my $i       = 0;
    my $max_val = 0;
    my %totals;

    while ( my $r = $rs->next ) {
        push @{ $results[0] }, $r->get_column('day');

        for my $i ( 0 .. $#metrics ) {
            my ($metric) = $metrics[$i];

            my $value = $r->get_column( $metric->[0] );

            $c->log->info( '[' . $metric->[0] . '][' . $r->get_column('day') . '][' . $value . ']');

            push @{ $results[ $i + 1 ] }, $value;
            $max_val = $value
              if $value > $max_val;
            $totals{'-'} += $value;
            $totals{ $metric->[0] } += $value;
        }
    }

    my $graph = GD::Graph::bars->new( 900, 500 );

    # make the y-ticks whole numbers and give a little padding for the top spot
    #    if($max_val < 10) {
    #        $max_val = 10;
    #    }
    #    else {
    #        my $fc = substr($max_val, 0, 1);
    #        $max_val =~ s/./0/g;
    #        $max_val =~ s/^./$fc/;
    #        $max_val = int(($max_val * 1.1) + .5);
    #    }
    #    $max_val++ while($max_val % 5 != 0);
    #    $c->log->info("REASONS REPORT MAX VALUE [$max_val]");

    $graph->set(
        title            => 'Mail Statistics Reasons',
        show_values      => 1,
        bar_spacing      => 2,
        bargroup_spacing => 6,
        y_min_value      => 0,

        #        y_max_value      => $max_val,
        #        y_tick_number    => 5,
        transparent      => 0,
        x_label          => 'Date',
        x_label_position => 1 / 2,
        y_label          => 'Number of Messages',
        y_label_position => 1 / 2,
        legend_placement => 'BC',
        values_vertical  => 1,
        y_number_format  => sub {
            my $number = shift;
            1 while $number =~ s/^(-?\d+)(\d\d\d)/$1,$2/;
            return $number;
        },
        values_format => sub {
            my $number = shift;
            1 while $number =~ s/^(-?\d+)(\d\d\d)/$1,$2/;
            return $number;
        },
    );

    $graph->set(
        dclrs => [
            qw( gray black blue gold yellow green red purple orange pink marine cyan lgray lblue lyellow lgreen lred lpurple lorange lbrown dgray dblue dyellow dgreen dred dpurple dpink dbrown )
        ],
        borderclrs => undef
    );
    $graph->set_y_label_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 14 );
    $graph->set_y_axis_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 10 );
    $graph->set_x_label_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 14 );
    $graph->set_x_axis_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 10 );
    $graph->set_legend_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 10 );
    $graph->set_title_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 18 );
    $graph->set_values_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 6 );

    $graph->set_legend(
        map {
            (
                $totals{'-'}
                ? int( ( ( $totals{ $_->[0] } / $totals{'-'} ) * 100 ) + .5 )
                : 0
              )
              . '% '
              . $_->[1]
          } @metrics
    );

    my $gd = $graph->plot( \@results ) or die $graph->error;

    $c->stash->{gd_image} = $gd;
}

sub actions : Local {
    my ( $self, $c ) = @_;

    my $cluster    = $c->req->param('cluster');
    my $domain     = $c->req->param('domain');
    my $user       = $c->req->param('user');
    my $start_date = $c->req->param('start_date');
    my $end_date   = $c->req->param('end_date');

    my ( $upper_date, $lower_date );

    my @x = qw(month day year);
    if ($end_date) {
        my @y = split /\D/, $end_date;
        $upper_date = DateTime->new( mesh @x, @y );
    }
    else {
        $upper_date = DateTime->now()->subtract( days => 1 );
    }

    if ($start_date) {
        my @y = split /\D/, $start_date;
        $lower_date = DateTime->new( mesh @x, @y );
    }
    else {
        $lower_date = $upper_date->clone->subtract( days => 7 );
    }

    $upper_date->set_hour(23)->set_minute(59)->set_second(59)
      ->set_nanosecond(999999999);
    $lower_date->set_hour(0)->set_minute(0)->set_second(0)->set_nanosecond(0);

    my $search_criteria =
      { day => { -between => [ $lower_date, $upper_date ] }, };

    $search_criteria->{cluster} = $cluster if $cluster;
    $search_criteria->{domain} = $domain
      if $domain and not $user;
    $search_criteria->{user} = $user if $user;

    my $table =
        ($user)   ? 'ActionsReasonsUser'
      : ($domain) ? 'ActionsReasonsDomain'
      :             'ActionsReasonsAll';

    $c->log->info("table [$table]");

    my $maillog = $c->model($table);
    my $rs      = $maillog->search(
        {%$search_criteria},
        {
            select => [
                'day',
                {
                    sum =>
"case when action in ('Blocked', 'Deferred') then number else 0 end"
                },
                {
                    sum =>
"case when action in ('Quarantined', 'Per-User Quarantine') then number else 0 end"
                },
                {
                    sum =>
"case when action in ('Allowed', 'Tagged') then number else 0 end"
                },
                {
                    sum =>
"case when action in ('Whitelisted') then number else 0 end"
                }
            ],
            as       => [qw( day blocked quarantined allowed whitelisted )],
            group_by => [qw( day )],
            order_by => [qw( day )],
        }
    );

    my @results = ( [], [], [], [], [] );
    my $i       = 0;
    my $max_val = 0;
    my ( $total, $total_blocked, $total_allowed, $total_quarantined,
        $total_whitelisted )
      = ( 0, 0, 0, 0, 0 );
    while ( my $r = $rs->next ) {
        $c->log->info(
            join( ' ',
                map { $r->get_column($_) }
                  qw( day blocked quarantined allowed whitelisted ) )
        );
        push @{ $results[0] }, $r->get_column('day');

        push @{ $results[1] }, $r->get_column('blocked');
        $max_val = $r->get_column('blocked')
          if $r->get_column('blocked') > $max_val;
        $total         += $r->get_column('blocked');
        $total_blocked += $r->get_column('blocked');

        push @{ $results[2] }, $r->get_column('quarantined');
        $max_val = $r->get_column('quarantined')
          if $r->get_column('quarantined') > $max_val;
        $total             += $r->get_column('quarantined');
        $total_quarantined += $r->get_column('quarantined');

        push @{ $results[3] }, $r->get_column('allowed');
        $max_val = $r->get_column('allowed')
          if $r->get_column('allowed') > $max_val;
        $total         += $r->get_column('allowed');
        $total_allowed += $r->get_column('allowed');

        push @{ $results[4] }, $r->get_column('whitelisted');
        $max_val = $r->get_column('whitelisted')
          if $r->get_column('whitelisted') > $max_val;
        $total             += $r->get_column('whitelisted');
        $total_whitelisted += $r->get_column('whitelisted');
    }

    my $graph = GD::Graph::bars->new( 900, 500 );

    $c->log->info("ACTIONS REPORT MAX VALUE [$max_val]");

    #    if($max_val < 10) {
    #        $max_val = 10;
    #    }
    #    else {
    #        $max_val = int(($max_val * 1.1) + .5);
    #    $c->log->info("ACTIONS REPORT MAX VALUE [$max_val]");
    #        my $fc = substr($max_val, 0, 1);
    #        $max_val =~ s/./0/g;
    #    $c->log->info("ACTIONS REPORT MAX VALUE [$max_val]");
    #        $max_val =~ s/^./$fc/;
    #    $c->log->info("ACTIONS REPORT MAX VALUE [$max_val]");
    #    }
    #    $max_val++ while($max_val % 5 != 0);
    #    $c->log->info("ACTIONS REPORT MAX VALUE [$max_val]");

    $graph->set(
        title            => 'Mail Statistics',
        show_values      => 1,
        bar_spacing      => 2,
        bargroup_spacing => 6,
        y_min_value      => 0,

        #       y_max_value      => $max_val,
        #       y_tick_number    => 5,
        transparent      => 0,
        x_label          => 'Date',
        x_label_position => 1 / 2,
        y_label          => 'Number of Messages',
        y_label_position => 1 / 2,
        legend_placement => 'BC',
        values_vertical  => 1,
        y_number_format  => sub {
            my $number = shift;
            1 while $number =~ s/^(-?\d+)(\d\d\d)/$1,$2/;
            return $number;
        },
        values_format => sub {
            my $number = shift;
            1 while $number =~ s/^(-?\d+)(\d\d\d)/$1,$2/;
            return $number;
        },
    );
    $graph->set( dclrs => [qw( red yellow green gray )], borderclrs => undef );
    $graph->set_y_label_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 14 );
    $graph->set_y_axis_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 10 );
    $graph->set_x_label_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 14 );
    $graph->set_x_axis_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 10 );
    $graph->set_legend_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 10 );
    $graph->set_title_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 18 );
    $graph->set_values_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 6 );

    $graph->set_legend(
        int( ( ( $total_blocked / $total ) * 100 ) + .5 ) . '% Blocked',
        int( ( ( $total_quarantined / $total ) * 100 ) + .5 ) . '% Quarantined',
        int( ( ( $total_allowed / $total ) * 100 ) + .5 ) . '% Allowed',
        int( ( ( $total_whitelisted / $total ) * 100 ) + .5 ) . '% Whitelisted'
    );

    my $gd = $graph->plot( \@results ) or die $graph->error;

    $c->stash->{gd_image} = $gd;
}

sub end : Private {
    my ( $self, $c ) = @_;

    $c->forward('ReefStats::View::GD');
}

sub top_reasons : Local {
    my ( $self, $c ) = @_;

    my $cluster    = $c->req->param('cluster');
    my $domain     = $c->req->param('domain');
    my $user       = $c->req->param('user');
    my $start_date = $c->req->param('start_date');
    my $end_date   = $c->req->param('end_date');

    my ( $upper_date, $lower_date );

    my @x = qw(month day year);
    if ($end_date) {
        my @y = split /\D/, $end_date;
        $upper_date = DateTime->new( mesh @x, @y );
    }
    else {
        $upper_date = DateTime->now()->subtract( days => 1 );
    }

    if ($start_date) {
        my @y = split /\D/, $start_date;
        $lower_date = DateTime->new( mesh @x, @y );
    }
    else {
        $lower_date = $upper_date->clone->subtract( days => 7 );
    }

    $upper_date->set_hour(23)->set_minute(59)->set_second(59)
      ->set_nanosecond(999999999);
    $lower_date->set_hour(0)->set_minute(0)->set_second(0)->set_nanosecond(0);

    my $search_criteria =
      { day => { -between => [ $lower_date, $upper_date ] }, };

=pod
    my @metrics = (
        [ blacklist        => Blacklist          => q~'Blacklist'~ ],
        [ spam_fingerprint => 'SPAM Fingerprint' => q~'SPAM Fingerprint'~ ],
        [
            invalid_recipient => 'Invalid Recipient' =>
              q~'Recipient Address Rejected','Invalid Recipient'~
        ],
        [
            intention_analysis => 'Intention Analysis' =>
              q~'Intention Analysis'~
        ],
        [
            header_filter_match => 'Header Filter Match' =>
              q~'Header Filter Match'~
        ],
        [
            barracuda_blocklist => 'Barracuda Blocklist' =>
              q~'Barracuda Blocklist'~
        ],
        [ timeout_exceeded => 'Timeout Exceeded' => q~'Timeout Exceeded'~ ],
        [ allowed          => Allowed            => q~'(Allowed)'~ ],
        [
            barracuda_whitelist => 'Barracuda Whitelist' =>
              q~'Barracuda Whitelist'~
        ],
        [
            recipient_whitelist => 'Recipient Whitelist' =>
              q~'Sender Block/Accept', 'Sender Address Rejected') and action in ('Whitelist'~
        ],
        [
            other => 'Other' =>
q~'Domain Not Found', 'Mail Protocol Error', 'Sender Address Rejected', 'Sender Block/Accept', 'Subject Filter Match', 'Virus', 'Client IP', 'Banned Attachment', 'No Such Domain', ''~
        ],
        [ score => 'Score' => q~'Score'~ ],
        [
            recipient_blocked => 'Recipient Blocked' =>
              q~'Sender Block/Accept') and action in ('Blocked'~
        ],
    );

=cut

    $search_criteria->{cluster} = $cluster if $cluster;
    $search_criteria->{domain} = $domain
      if $domain and not $user;
    $search_criteria->{user} = $user if $user;

    my $table =
        ($user)   ? 'ActionsReasonsUser'
      : ($domain) ? 'ActionsReasonsDomain'
      :             'ActionsReasonsAll';

    $c->log->info("table [$table]");

    my $maillog = $c->model($table);

    my $rs = $maillog->search(
        {%$search_criteria},
        {
            select   => [ 'reason', { sum => 'number' } ],
            as       => [qw(reason number)],
            group_by => [qw(reason)],
            order_by => ['2 desc'],
        }
    );

    my @results = ( [], [] );

    while ( my $r = $rs->next ) {
        push @{ $results[0] }, $r->get_column('reason');
        push @{ $results[1] }, $r->get_column('number');
    }

    my $graph = GD::Graph::pie->new( 900, 500 );

    $graph->set(
        title            => 'Reason Breakdown',
    );

    $graph->set(
        dclrs => [
            qw( gray black blue gold yellow green red purple orange pink marine cyan lgray lblue lyellow lgreen lred lpurple lorange lbrown dgray dblue dyellow dgreen dred dpurple dpink dbrown )
        ],
    );
    $graph->set_title_font(
        '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 18 );

    my $gd = $graph->plot( \@results ) or die $graph->error;

    $c->stash->{gd_image} = $gd;

}

1;
